package com.stars.easyms.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.encrypt.EasyMsEncrypt;
import com.stars.easyms.base.http.EasyMsResponseEntity;
import com.stars.easyms.base.http.EasyMsRequestSynchronizationManager;
import com.stars.easyms.base.util.DateTimeUtil;
import com.stars.easyms.gateway.constant.EasyMsGatewayConstants;
import com.stars.easyms.gateway.properties.EasyMsGatewayProperties;
import com.stars.easyms.jwt.properties.EasyMsJwtProperties;
import com.stars.easyms.jwt.provider.EasyMsJwtProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * <p>className: EasyMsAuthorizationFilter</p>
 * <p>description: jwt权限检验过滤器</p>
 * <p>description: 可以通过spring.jwt.enabled=false关闭jwt校验，但是需要在请求头中设置userInfo</p>
 *
 * @author guoguifang
 * @version 1.6.1
 * @date 2020/8/14 13:20
 */
@Slf4j
public class EasyMsAuthorizationFilter implements GlobalFilter, Ordered {

    @Autowired
    private EasyMsJwtProperties jwtProperties;

    @Autowired
    private EasyMsJwtProvider jwtProvider;

    @Autowired
    private EasyMsGatewayProperties gatewayProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 获取请求和响应
        ServerHttpRequest request = exchange.getRequest();

        // 判断是否开启了jwt验证，如果没有开启则直接跳过
        if (!jwtProvider.isNeedAuthenticate(request.getPath().value())) {
            return chain.filter(exchange);
        }

        // 如果开启了debug模式则把debug的url直接跳过权限校验
        if (exchange.getAttribute(EasyMsGatewayConstants.DEBUG) != null) {
            return chain.filter(exchange);
        }

        // 如果开启了swagger的debugger模式则不做权限校验
        if (gatewayProperties.isDebugEnabled() && exchange.getAttribute(EasyMsGatewayConstants.SWAGGER_DEBUG) != null) {
            return chain.filter(exchange);
        }

        // 获取请求头
        HttpHeaders headers = request.getHeaders();

        // 请求头中获取令牌
        String token = headers.getFirst(jwtProperties.getHeaderKey());

        // 判断请求头中是否有令牌，如果没有令牌响应中放入返回的状态码：没有权限访问
        if (StringUtils.isBlank(token)) {
            return unauthorized(exchange);
        }

        // 如果请求头中有令牌则解析令牌
        String userInfo = jwtProvider.getAuthentication(token);

        // 如果userInfo为空则解析异常，说明令牌过期或者伪造等不合法情况出现
        if (userInfo == null) {
            return unauthorized(exchange);
        }

        // 若不为空则把userInfo打印到日志中并存放到请求头中
        log.info("当前请求的用户信息: {}", userInfo);
        return chain.filter(
                exchange.mutate()
                        .request(request.mutate()
                                .header(HttpHeaderConstants.HEADER_KEY_USER_INFO,
                                        Base64.encodeBase64String(userInfo.getBytes(StandardCharsets.UTF_8)))
                                .build())
                        .build());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1002;
    }

    /**
     * 返回没有权限访问的Json格式字符串
     */
    private Mono<Void> unauthorized(ServerWebExchange exchange) {

        // 获取无权限访问返回对象并转成json字符
        EasyMsResponseEntity responseEntity = new EasyMsResponseEntity();
        responseEntity.setRetCode(String.valueOf(HttpStatus.UNAUTHORIZED.value()));
        responseEntity.setRetMsg(HttpStatus.UNAUTHORIZED.getReasonPhrase());
        String responseData = JSON.toJSONString(responseEntity);

        // 获取返回字符串的字节码，如果需要加密则返回的是加密后的字节码
        byte[] responseContent;
        if (gatewayProperties.getEncrypt().isEnabled() && exchange.getAttribute(EasyMsGatewayConstants.SWAGGER_DEBUG) == null) {
            Object encryptedResponseData = EasyMsEncrypt.encrypt(responseData,
                    gatewayProperties.getEncrypt().getSecret(),
                    gatewayProperties.getEncrypt().getIv(),
                    gatewayProperties.getEncrypt().getResponseKey());
            responseContent = JSON.toJSONString(encryptedResponseData).getBytes(StandardCharsets.UTF_8);
        } else {
            responseContent = responseData.getBytes(StandardCharsets.UTF_8);
        }

        String traceId = exchange.getAttribute(HttpHeaderConstants.TRACE_KEY);
        String requestSys = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS);
        String currentRequestPath = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_PATH);
        String upstreamRequestId = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_UPSTREAM_REQUEST_ID);
        String downstreamRequestId = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_ID);
        EasyMsRequestSynchronizationManager.setTraceInfo(traceId, upstreamRequestId);

        // 记录接收服务日志信息
        log.info("[路由-返回响应]-[请求地址:{}]-[请求系统:{}]-[请求ID:{}]-[响应时间:{}]-响应数据:{}",
                currentRequestPath, requestSys, downstreamRequestId, DateTimeUtil.now(), responseData);

        EasyMsRequestSynchronizationManager.clearTraceInfo();

        // 将byte放入DataBuffer中并返回
        ServerHttpResponse response = exchange.getResponse();
        DataBuffer responseDataBuffer = response.bufferFactory().wrap(responseContent);
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        response.getHeaders().setContentLength(responseContent.length);
        return response.writeWith(Mono.just(responseDataBuffer));
    }

}
